﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class FinPlusSyncThrottle:FinPlusComponent
    {
        public override string Id { get; set; }
        public override string[] States { get { return new string[] { }; } }
        public override string[] TestStates { get { return new string[] { "ModelTest", "IsValid" }; } }
        public override IFinPlusFunctions Model { get { return _model; } }
        public override string ModelTest { get { return _model.ToString(); } }
        public override bool IsValid { get; set; }

        private enum Params{ Refresh, Resend };

        private IFinPlusQueueThrottle _throttle;
        private IFinPlusComponent _sourceConn;
        private IFinPlusFunctions _model;

        private readonly IDictionary<string, string> _throttleItems;
        private readonly IList<string> _syncItems;
        private bool _hasInitialised, _withResend;

        //construct
        public FinPlusSyncThrottle(string syncItems, string throttleItems, string throttle, string withResend)
        {
            _syncItems = syncItems.Split(',').ToList();
            _throttleItems = u.ToDictionary(throttleItems, true);
            _withResend = bool.Parse(withResend);
            _throttle = new FinPlusQueueThrottle(double.Parse(throttle), true, _withResend);
        }

        //common control interface
        public override void Dock(object component, string dock)
        {
            try
            {
                _sourceConn = (IFinPlusComponent)component;
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void Initialise()
        {
            try
            {
                if (_hasInitialised) return;

                _throttle.Start(Update);

                foreach (var throttleItem in _throttleItems)
                    _sourceConn.Model.Get(throttleItem.Key, new Function(), Item_Updated);

                _model = _sourceConn.Model.Clone();
                _model.Get(Params.Resend.ToString(), string.Empty, Item_Resend);
                _model.Get(Params.Refresh.ToString(), string.Empty);

                _throttle.IsBusy = false;
                IsValid = _hasInitialised = true;
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void CommandRouter(IFinPlusFunction func) { }

        public override void Dispose()
        {
            if (_sourceConn != null)
                _sourceConn.Dispose();
        }

        //private 
        private void Update(IDictionary<string, object> updates)
        {
            try
            {
                var funcs = UpdateModel(updates);
                
                var syncs = SyncModel(funcs);

                //TODO reponse resync not finished

                _throttle.IsBusy = false;
            }
            catch (Exception e)
            {
                _throttle.IsBusy = IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private IDictionary<int, IFinPlusFunction> UpdateModel(IDictionary<string, object> updates)
        {
            var funcs = new Dictionary<int, IFinPlusFunction>();
            foreach (IFinPlusItem update in updates.Values)
            {
                if (!_model.ContainsKey(update.Id)) continue;
                
                var func = _model[update.Id];
                var item = func.Get(update.Name);
                
                if (item == null) continue;
                   
                item.Set(update.Value);
                funcs[func.Id] = func;
            }

            return funcs;
        }

        private IList<string> SyncModel(IDictionary<int, IFinPlusFunction> funcs)
        {
            var syncs = new List<string>();

            foreach (var syncItem in _syncItems)
            {
                foreach (var func in funcs.Values)
                {
                    var item = func.Get(syncItem);
                    if (item == null || string.IsNullOrEmpty(item.Value)) continue;

                    foreach (var value in item.Value.Split(','))
                    {
                        if (syncs.Contains(value)) continue;

                        func.Get(Params.Refresh.ToString()).Set(value);
                        syncs.Add(value);
                    }
                }
            }

            return syncs;
        }

        //events
        private void Item_Updated(object sender, PropertyChangedEventArgs a)
        {
            var item = (IFinPlusItem)sender;

            if (!_throttleItems.ContainsKey(item.Name)) return;

             var clone = item.Clone(item.Id);
            _throttle.Put(clone.Key(_throttleItems[item.Name]), clone, clone.TimeStamp);
        }

        private void Item_Resend(object sender, PropertyChangedEventArgs a)
        {
            var item = (IFinPlusItem)sender;
            var key = string.Format("{0}|{1}|", item.Id, item.Value);
            _throttle.Resend(key);
        }
    }
}
